﻿using ssize_t = size_t;
#include <stddef.h>
#include <stdint.h>
extern "C" {
#include <vlc/vlc.h>
}
#include "VideoItem.h"
#include <QSGGeometryNode>
#include <QSGMaterial>
#include <QSGTexture>
#include <QSGSimpleTextureNode>


VideoItem::VideoItem(QQuickItem* parent /*= nullptr*/)
	: QQuickItem(parent)
	, m_image(QSize(1920, 1080), QImage::Format_RGBA8888)
{
	setFlag(ItemHasContents, true);
	const char* args[] = {
	"--verbose=0"
	};
	m_vlc = libvlc_new(sizeof(args) / sizeof(*args), args);
}

VideoItem::~VideoItem() {
	cleanup();
}

bool VideoItem::play() {
	stop();
	QString urlStr = m_source;
	if (!QUrl(m_source).isValid()) {
		urlStr = QUrl::fromLocalFile(m_source).toString();
	}
	m_media = libvlc_media_new_location(m_vlc, urlStr.toLocal8Bit().data());
	if (!m_media) {
		qWarning() << QString("unable to create media %1").arg(urlStr);
		return false;
	}
	m_mp = libvlc_media_player_new_from_media(m_media);
	if (!m_mp) {
		qWarning() << QString("unable to create media player");
		libvlc_media_release(m_media);
		return false;
	}
	libvlc_media_player_set_time(m_mp, 2000);
	libvlc_video_set_callbacks(m_mp, &VideoItem::lock, &VideoItem::unlock, &VideoItem::display, this);
	libvlc_video_set_format(m_mp, "RGBA", 1920, 1080, 1920 * 4);

	//对libvlc绑定错误事件
	libvlc_event_manager_t* eventManager = libvlc_media_player_event_manager(m_mp);
	libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached, &VideoItem::error, this);
	libvlc_event_attach(eventManager, libvlc_MediaPlayerEncounteredError, &VideoItem::error, this);
	libvlc_event_attach(eventManager, libvlc_MediaPlayerPlaying, &VideoItem::onPlaying, this);
	libvlc_event_attach(eventManager, libvlc_MediaPlayerStopped, &VideoItem::onStopped, this);
	libvlc_media_add_option(m_media, "vcd-caching=200");
	libvlc_media_add_option(m_media, "sout-rtp-caching=200");
	libvlc_media_add_option(m_media, "network-caching=200");
	libvlc_media_add_option(m_media, "clock-jitter=0");
	libvlc_media_add_option(m_media, "clock-synchro=0");
	libvlc_media_add_option(m_media, "clock-master=0");
	libvlc_media_add_option(m_media, "clock-source=0");
	libvlc_media_add_option(m_media, "clock-description=monotonic");
	if (m_pPixelData == nullptr) {
		m_pPixelData = new unsigned char[1920 * 1080 * 4];
	}
	// Play the video
	libvlc_media_player_play(m_mp);
	return true;
}

void VideoItem::stop() {
	if (m_mp) {
		libvlc_media_player_stop(m_mp);
		libvlc_media_player_release(m_mp);
		m_mp = nullptr;
	}
	if (m_media) {
		libvlc_media_release(m_media);
		m_media = nullptr;
	}
	m_hasError = false;
	emit errorChanged();
}

bool VideoItem::playing() const {
	return m_playing;
}

bool VideoItem::hasError() const {
	return hasError();
}

QString VideoItem::source() const {
	return m_source;
}

void VideoItem::setSource(QString const& source) {
	if (m_source != source) {
		m_source = source;		
		emit sourceChanged();
		if (m_autoPlayAsSourceChanged && !m_source.isEmpty()) {
			play();
		}
	}
}

bool VideoItem::autoPlayAsSourceChanged() const {
	return m_autoPlayAsSourceChanged;
}

void VideoItem::setAutoPlayAsSourceChanged(bool autoPlayAsSourceChanged) {
	if (m_autoPlayAsSourceChanged != autoPlayAsSourceChanged) {
		m_autoPlayAsSourceChanged = autoPlayAsSourceChanged;
		emit setAutoPlayAsSourceChangedChanged();
		if (m_autoPlayAsSourceChanged && !m_source.isEmpty()) {
			play();
		}
	}
}

void VideoItem::drawFrame() {
	if (!m_node) {
		return;
	}	
	if (m_frameChanged) {
		m_image = QImage(m_pPixelData, 1920, 1080, QImage::Format_RGBA8888);
		static_cast<QSGSimpleTextureNode*>(m_node)->setRect(boundingRect());
		if (m_texture) {
			delete m_texture;
			m_texture = nullptr;
		}
		m_texture = window()->createTextureFromImage(m_image);
		m_node->setTexture(m_texture);
		m_node->markDirty(QSGNode::DirtyGeometry);		
		m_frameChanged = false;
	}
}

QSGNode* VideoItem::updatePaintNode(QSGNode* old, UpdatePaintNodeData*) {
	QSGSimpleTextureNode* node = static_cast<QSGSimpleTextureNode*>(old);
	if (m_frameChanged) {
		if (!node) {
			node = new QSGSimpleTextureNode;
			m_texture = window()->createTextureFromImage(m_image);
			node->setTexture(m_texture);
		}
	}
	node->setRect(boundingRect());
	if (!node) {
		m_frameChanged = false;
		return nullptr;
	}
	m_node = node;
	drawFrame();
	m_node = nullptr;
	return node;
}

void VideoItem::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
	m_geometryChanged = true;
	update(); 
	QQuickItem::geometryChange(newGeometry, oldGeometry);	
}

void VideoItem::cleanup() {
	stop();
	if (m_vlc)
		libvlc_release(m_vlc);
	m_vlc = nullptr;
}

void* VideoItem::lock(void* data, void** p_pixels) {
	VideoItem* p = static_cast<VideoItem*>(data);
	if (p) {
		*p_pixels = p->m_pPixelData;
		p->m_mutex.lock();
	}
	return nullptr;
}

void VideoItem::unlock(void* data, void* id, void* const* p_pixels) {
	VideoItem* p = static_cast<VideoItem*>(data);
	if (p) {
		p->m_mutex.unlock();		
	}
}

void VideoItem::display(void* data, void* id) {
	VideoItem* player = reinterpret_cast<VideoItem*>(data);
	if (player) {
		player->m_frameChanged = true;
		QMetaObject::invokeMethod(player, "update");
	}
}

void VideoItem::error(const struct libvlc_event_t* p_event, void* data) {
	VideoItem* player = reinterpret_cast<VideoItem*>(data);
	if (player) {
		player->m_hasError = true;
		emit player->errorChanged();
		QMetaObject::invokeMethod(player, "stop");
		emit player->errorOccurred();
	}
}

void VideoItem::onPlaying(const struct libvlc_event_t* p_event, void* data) {
	VideoItem* player = reinterpret_cast<VideoItem*>(data);
	if (player) {
		player->m_playing = true;
		emit player->playingChanged();
	}
}

void VideoItem::onStopped(const struct libvlc_event_t* p_event, void* data) {
	VideoItem* player = reinterpret_cast<VideoItem*>(data);
	if (player) {
		player->m_playing = false;
		emit player->playingChanged();
	}
}

//#include "VideoItem.moc"